#pragma once

#include "Value.h"

using namespace Platform;
using namespace Windows::Storage;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;

namespace mbase {

ref class Expression {
public:
	virtual Value^ evaluate(IMap<String^,Value^>^ vars) { return nullptr; }
	//Expression^ operator+(Expression^ e);
	//Expression^ operator+(int e);
	//Expression^ operator+(float e);
	//Expression^ operator+(String^ e);
	Expression^ operator==(Expression^ e);
	Expression^ operator==(int e);
	Expression^ operator==(float e);
	Expression^ operator==(String^ e);
	Expression^ operator!=(Expression^ e);
	Expression^ operator!=(int e);
	Expression^ operator!=(float e);
	Expression^ operator!=(String^ e);
	Expression^ operator<(Expression^ e);
	Expression^ operator<(int e);
	Expression^ operator<(float e);
	Expression^ operator<(String^ e);
	Expression^ operator>(Expression^ e);
	Expression^ operator>(int e);
	Expression^ operator>(float e);
	Expression^ operator>(String^ e);
	Expression^ operator<=(Expression^ e);
	Expression^ operator<=(int e);
	Expression^ operator<=(float e);
	Expression^ operator<=(String^ e);
	Expression^ operator>=(Expression^ e);
	Expression^ operator>=(int e);
	Expression^ operator>=(float e);
	Expression^ operator>=(String^ e);
	Expression^ operator!();
	Expression^ operator&&(Expression^ e);
	Expression^ operator||(Expression^ e);
};

/*ref class PlusExpression : Expression {
private:
	Expression^ expr1;
	Expression^ expr2;
public:
	PlusExpression(Expression^ e1, Expression^ e2) : expr1(e1), expr2(e2) {}
	Value^ evaluate(IMap<String^,Value^>^ vars) { return expr1->evaluate(vars) + expr2->evaluate(vars); }
};*/

ref class NotExpression : Expression {
private:
	Expression^ expr1;
public:
	NotExpression(Expression^ e1) : expr1(e1) {}
	Value^ evaluate(IMap<String^,Value^>^ vars) { return !expr1->evaluate(vars); }
};

ref class EqualExpression : Expression {
private:
	Expression^ expr1;
	Expression^ expr2;
public:
	EqualExpression(Expression^ e1, Expression^ e2) : expr1(e1), expr2(e2) {}
	Value^ evaluate(IMap<String^,Value^>^ vars) { return expr1->evaluate(vars) == expr2->evaluate(vars); }
};

ref class AndExpression : Expression {
private:
	Expression^ expr1;
	Expression^ expr2;
public:
	AndExpression(Expression^ e1, Expression^ e2) : expr1(e1), expr2(e2) {}
	Value^ evaluate(IMap<String^,Value^>^ vars) { return expr1->evaluate(vars) && expr2->evaluate(vars); }
};

ref class OrExpression : Expression {
private:
	Expression^ expr1;
	Expression^ expr2;
public:
	OrExpression(Expression^ e1, Expression^ e2) : expr1(e1), expr2(e2) {}
	Value^ evaluate(IMap<String^,Value^>^ vars) { return expr1->evaluate(vars) || expr2->evaluate(vars); }
};

ref class NotEqualExpression : Expression {
private:
	Expression^ expr1;
	Expression^ expr2;
public:
	NotEqualExpression(Expression^ e1, Expression^ e2) : expr1(e1), expr2(e2) {}
	Value^ evaluate(IMap<String^,Value^>^ vars) { return expr1->evaluate(vars) != expr2->evaluate(vars); }
};

ref class LessExpression : Expression {
private:
	Expression^ expr1;
	Expression^ expr2;
public:
	LessExpression(Expression^ e1, Expression^ e2) : expr1(e1), expr2(e2) {}
	Value^ evaluate(IMap<String^,Value^>^ vars) { return expr1->evaluate(vars) < expr2->evaluate(vars); }
};

ref class GreaterExpression : Expression {
private:
	Expression^ expr1;
	Expression^ expr2;
public:
	GreaterExpression(Expression^ e1, Expression^ e2) : expr1(e1), expr2(e2) {}
	Value^ evaluate(IMap<String^,Value^>^ vars) { return expr1->evaluate(vars) > expr2->evaluate(vars); }
};

ref class LessEqualExpression : Expression {
private:
	Expression^ expr1;
	Expression^ expr2;
public:
	LessEqualExpression(Expression^ e1, Expression^ e2) : expr1(e1), expr2(e2) {}
	Value^ evaluate(IMap<String^,Value^>^ vars) { return expr1->evaluate(vars) <= expr2->evaluate(vars); }
};

ref class GreaterEqualExpression : Expression {
private:
	Expression^ expr1;
	Expression^ expr2;
public:
	GreaterEqualExpression(Expression^ e1, Expression^ e2) : expr1(e1), expr2(e2) {}
	Value^ evaluate(IMap<String^,Value^>^ vars) { return expr1->evaluate(vars) >= expr2->evaluate(vars); }
};

ref class AtomicExpression : Expression {
private:
	Value^ value;
public:
	AtomicExpression(Value^ v) : value(v) {}
	Value^ evaluate(IMap<String^,Value^>^ vars) { return value; }
};

ref class NumPropertyExpression : Expression {
private:
	Expression^ expr;
	String^ prop;
public:
	NumPropertyExpression(Expression^ e, String^ p) : expr(e), prop(p) {}
	Value^ evaluate(IMap<String^,Value^>^ vars) {
		return ref new IntValue(expr->evaluate(vars)->asObject()->GetNumProperty(prop)->N);
	}
	//Expression^ operator==(int e);
};

ref class FltPropertyExpression : Expression {
private:
	Expression^ expr;
	String^ prop;
public:
	FltPropertyExpression(Expression^ e, String^ p) : expr(e), prop(p) {}
	Value^ evaluate(IMap<String^,Value^>^ vars) {
		return ref new FltValue(expr->evaluate(vars)->asObject()->GetFltProperty(prop)->N);
	}
	//Expression^ operator==(float e);
};

ref class StrPropertyExpression : Expression {
private:
	Expression^ expr;
	String^ prop;
public:
	StrPropertyExpression(Expression^ e, String^ p) : expr(e), prop(p) {}
	Value^ evaluate(IMap<String^,Value^>^ vars) {
		return ref new StringValue(expr->evaluate(vars)->asObject()->GetStrProperty(prop));
	}
	//Expression^ operator==(String^ e);
};

ref class ObjPropertyExpression : Expression {
private:
	Expression^ expr;
	String^ prop;
public:
	ObjPropertyExpression(Expression^ e, String^ p) : expr(e), prop(p) {}
	Value^ evaluate(IMap<String^,Value^>^ vars) {
		return ref new ObjectValue(expr->evaluate(vars)->asObject()->GetObjProperty(prop));
	}
};

ref class VariableExpression : Expression {
protected:
	String^ var;
public:
	String^ GetVarName() { return var; }
	VariableExpression(String^ v) : var(v) {}
	Value^ evaluate(IMap<String^,Value^>^ vars) { return vars->Lookup(var); }
	Expression^ GetStrProperty(String^ name) { return ref new StrPropertyExpression(this, name); }
	Expression^ GetNumProperty(String^ name) { return ref new NumPropertyExpression(this, name); }
	Expression^ GetFltProperty(String^ name) { return ref new FltPropertyExpression(this, name); }
	Expression^ GetObjProperty(String^ name) { return ref new ObjPropertyExpression(this, name); }
};

/*ref class PropertyExpression : VariableExpression {
private:
	mbase::Expression^ expr;
public:
	PropertyExpression(mbase::Expression^ e, String^ p) : expr(e), mbase::VariableExpression(p) {}
	Value^ evaluate(IMap<String^,Value^>^ vars) {
		return ref new mbase::ObjectValue(expr->evaluate(vars)->asObject()->GetObjProperty(var));
	}
};*/

}
